package rabid;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import common.IndexWordUtil;
import common.StopWordFile;
import common.UserInteractionException;

/**
 * Communicates with the Index to get the necessary
 * data for each RABID command
 * 
 * @author Andrew Bernard
 */
public class IndexManager {   
  private Index index = null;
  private long corpusFileSize;
  private StopWordFile stopWordFile;
  private IndexWordUtil wordUtil;
  private char[] breakChars;
  
  /**
   * Constructor. Creates a new instance of Index and calculates
   * the size of the corpus.
   */
  public IndexManager() throws FileNotFoundException, IOException {
    index = new Index();
    
    Iterator itr = index.getDataHeaderList().iterator();
    while(itr.hasNext()) {
      Index.DataHeader dataHeader = (Index.DataHeader)itr.next();
      File file = new File(dataHeader.fileName);
      corpusFileSize += file.length();
    }
    breakChars = index.getHeader().breakChars.toCharArray();
    wordUtil = new IndexWordUtil(breakChars);
    stopWordFile = new StopWordFile(new File(index.getHeader().stopFilePath), breakChars);
    
  }
  
  public long getCorpusFileSize() {
    return corpusFileSize;
  }
  
  public long getIndexFileSize() {
    return index.getHeader().indexFileSize;
  }
  
  public boolean isStopWord(String word) {
    return stopWordFile.isStopWord(word);
  }
    
  public String getCorpusDetails() {
    NumberFormat numberText = NumberFormat.getIntegerInstance();
    StringBuffer stats = new StringBuffer();
//    stats.append("\n-------- Corpus Files --------");
//    
//    Iterator itr = index.getDataHeaderList().iterator();
//    while(itr.hasNext()) {
//      Index.DataHeader dataHeader = (Index.DataHeader)itr.next();
//      stats.append("\n\nFile name: "+dataHeader.fileName)
//           .append("\nCharacters: "+numberText.format(dataHeader.charsInFile))
//           .append("\nWords: "+numberText.format(dataHeader.wordsInFile));
//    }
    
    Index.Summary summary = index.getSummary();
    stats.append("\n-------- Corpus Summary --------")
         .append("\nFiles: " + numberText.format(summary.filesInCorpus))
         .append("\nWords: " + numberText.format(summary.wordsInCorpus))
         .append("\nCharacters: " + numberText.format(summary.charsInCorpus));
    
    return stats.toString();
  }
  
  public String getIndexDetails() {
    StringBuffer details = new StringBuffer();
    Index.Header header = index.getHeader();
    details.append("\n-------- Index Summary --------")
           .append("\nPath: "+header.indexPath)
           .append("\nCreation Date: "+header.creationDate)
           .append("\nStop File Path: "+header.stopFilePath)
           .append("\nCustom break chars: "+header.breakChars)
           .append("\nIndex Size: ")
           .append(NumberFormat.getIntegerInstance().format(header.indexFileSize)).append(" bytes");
    
    return details.toString();
  }
  
  /**
   * This method does the following:
   *    1. Create a QueryWord object for each word in the query
   *    2. For each line in the index, set the word object to true or false if it exists in the line
   *    3. Apply the query pattern to each set of word objects for each line
   *    4. Store the results if the query returns a match
   *    
   * @return a List of Query.Result objects
   */
  public List runQuery(Query query) 
  throws IOException, UserInteractionException {
    List wordsInQuery = getQueryWords(query.toString());
    List results = new ArrayList();
    
    return results;
  }
  
  private QueryWord[] getLineWordMapping(List wordsInQuery, int lineFilter, int lineNum) {
    QueryWord[] lineWordMap = new QueryWord[wordsInQuery.size()];
    
    for(int w = 0; w < wordsInQuery.size(); w++) {
      String word = wordsInQuery.get(w).toString().toLowerCase();
      boolean foundPossibleMatch = index.isWordOnLine(lineFilter, lineNum, word);          
      lineWordMap[w] = new QueryWord(word, foundPossibleMatch);
    }    
    return lineWordMap;
  }
    
  /**
   * Returns a list of the words in the query as string objects.
   */
  private List getQueryWords(String query) 
  throws IOException, UserInteractionException {
    ArrayList words = new ArrayList();
    StringReader reader = new StringReader(query);
    for(String wordText = wordUtil.getNextWord(reader);
        wordText != null;
        wordText = wordUtil.getNextWord(reader)) {
      words.add(wordText);
    }    
    return words;
  }
  
  public void refineQuery(Query refinedQuery, List queryResults) 
  throws IOException, UserInteractionException {
    List wordsInQuery = getQueryWords(refinedQuery.toString());
    Iterator itrResults = queryResults.iterator();
    while(itrResults.hasNext()) {
      Query.Result result = (Query.Result)itrResults.next();
      QueryWord[] lineWordMap = getLineWordMapping(wordsInQuery, result.lineFilter, result.lineNum);
      if(!refinedQuery.evaluate(lineWordMap))
        itrResults.remove();
      else
        result.lineWordMap = lineWordMap;
    }
  }
  
  public boolean isWordInLine(String word, String line) {
    boolean wordFound = false;
    for(int c = 0; c < breakChars.length; c++) {
      String test = word + breakChars[c];
      if(line.indexOf(test) != -1) {
        wordFound = true;
        break;
      }
    }
    
    if(!wordFound) {
      //test if it's the last word in the string
      for(int c = 0; c < breakChars.length; c++) {
        String test = breakChars[c] + word;
        int testIndex = line.indexOf(test);
        if(testIndex != -1 && 
        //make sure the test is at the end of the line
        testIndex + test.length() >= line.length()) {
          wordFound = true;
          break;
        }
      }
    }
    
    return wordFound;
  }
}
